package lnk

import (
	"bytes"
	"encoding/binary"
	"io"
	"strings"

	"github.com/olekukonko/tablewriter"
)

//TrackerDataBlock
//BlockSize (4 bytes): A 32-bit, unsigned integer that specifies the size of the TrackerDataBlock structure. This value MUST be 0x00000060.
//BlockSignature (4 bytes): A 32-bit, unsigned integer that specifies the signature of the TrackerDataBlock extra data section. This value MUST be 0xA0000003.
//Length (4 bytes): A 32-bit, unsigned integer that specifies the size of the rest of the TrackerDataBlock structure, including this Length field. This value MUST be 0x00000058.
//Version (4 bytes): A 32-bit, unsigned integer. This value MUST be 0x00000000.
//MachineID (16 bytes): A NULL–terminated character string, as defined by the system default code page, which specifies the NetBIOS name of the machine where the link target was last known to reside.
//Droid (32 bytes): Two values in GUID packet representation ([MS-DTYP] section 2.3.4.2) that are used to find the link target with the Link Tracking service, as described in [MS-DLTW].
//DroidBirth (32 bytes): Two values in GUID packet representation that are used to find the link target with the Link Tracking service
type TrackerDataBlock struct {
	size       uint32
	signature  uint32
	Length     uint32
	Version    uint32
	MachineID  string
	Droid      []GUID
	DroidBirth []GUID
}

func (t *TrackerDataBlock) BlockSize() uint32 {
	return t.size
}

func (t *TrackerDataBlock) BlockSignature() uint32 {
	return t.signature
}

func (t *TrackerDataBlock) BlockType() string {
	return blockSignature(t.signature)
}

// String prints the TrackerDataBlock in a table.
func (t TrackerDataBlock) String() string {
	var sb strings.Builder
	sb.WriteString("\n")
	table := tablewriter.NewWriter(&sb)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetRowLine(true)

	table.SetHeader([]string{"TrackerDataBlock", "Value"})
	table.Append([]string{"BlockSize", uint32TableStr(t.size)})
	table.Append([]string{"BlockSignature", uint32TableStr(t.signature)})
	table.Append([]string{"BlockType", t.BlockType()})
	table.Append([]string{"Length", uint32TableStr(t.Length)})
	table.Append([]string{"Version", uint32TableStr(t.Version)})
	table.Append([]string{"MachineID", t.MachineID})
	for _, droid := range t.Droid {
		table.Append([]string{"Droid", droid.String()})
	}
	for _, droidBirth := range t.DroidBirth {
		table.Append([]string{"DroidBirth", droidBirth.String()})
	}
	table.Render()

	return sb.String()
}

func ParseTrackerDataBlock(size, signature uint32, data []byte) (*TrackerDataBlock, error) {
	r := bytes.NewReader(data)
	d := TrackerDataBlock{
		size:      size,
		signature: signature,
	}

	err := binary.Read(r, binary.LittleEndian, &d.Length)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.LittleEndian, &d.Version)
	if err != nil {
		return nil, err
	}
	machineId := make([]byte, 16)
	_, err = io.ReadFull(r, machineId)
	if err != nil {
		return nil, err
	}

	d.MachineID, _ = GBKDecoder.String(readString(machineId))
	droid := make([]byte, 32)
	_, err = io.ReadFull(r, droid)
	if err != nil {
		return nil, err
	}
	//d.Droid = droid
	guidByte := new([16]byte)
	copy(guidByte[:], droid[:16])
	droidGuid1 := makeGuidWithBytes(*guidByte)
	d.Droid = append(d.Droid, droidGuid1)
	copy(guidByte[:], droid[16:])
	droidGuid2 := makeGuidWithBytes(*guidByte)
	d.Droid = append(d.Droid, droidGuid2)

	droidBirth := make([]byte, 32)
	_, err = io.ReadFull(r, droidBirth)
	if err != nil {
		return nil, err
	}

	copy(guidByte[:], droidBirth[:16])
	droidBirthGuid1 := makeGuidWithBytes(*guidByte)
	d.DroidBirth = append(d.DroidBirth, droidBirthGuid1)
	copy(guidByte[:], droidBirth[16:])
	droidBirthGuid2 := makeGuidWithBytes(*guidByte)
	d.DroidBirth = append(d.DroidBirth, droidBirthGuid2)
	//d.DroidBirth, _ = GBKDecoder.String(string(droidBirth))

	return &d, nil
}
